//
//  VideoWriter.swift
//  WatermarkCamera
//
//  Created by AxcLogo on 4/7/2024.
//

import UIKit
import Foundation
import AxcBedrock

// MARK: - [VideoWriter]

extension VideoWriter {
    enum Orientation {
        case transform(_ transform: CGAffineTransform)

        case orientation(_ orientation: UIInterfaceOrientation)
    }

    enum WriterState {
        case end

        case being

        case pause
    }
}

// 视频采集写入，包含音频处理
// 仅用于视频文件生成和写入

extension VideoWriter {
    /// 开始采集视频
    /// - Parameters:
    ///   - fileUrl: 文件存储地址
    ///   - orientation: 方向
    ///   - articulation: 清晰度
    ///   - aspectRatio: 画幅
    ///   - encodingLiveVideo: 对视频进行编码
    ///   - shouldCollectAudio: 对音频进行采集
    func beginCollectVideo(fileUrl: URL,

                           orientation: Orientation = .orientation(.portrait),

                           articulation: VideoBuffer.Articulation,

                           aspectRatio: Eyepiece.AspectRatio,

                           encodingLiveVideo: Bool = true,

                           shouldCollectAudio: Bool = true) {
        writerState = .being

        delFragmentList()

        beginCollect(fileUrl: fileUrl,

                     orientation: orientation,

                     articulation: articulation,

                     aspectRatio: aspectRatio,

                     encodingLiveVideo: encodingLiveVideo,

                     shouldCollectAudio: shouldCollectAudio)
    }

    /// 暂停采集视频
    func pauseCollectVideo(_ finishBlock: @escaping AxcBlock.Empty) {
        writerState = .pause

        endCollect { [weak self] url in

            guard let weakSelf = self else { return }

            weakSelf.videoFragmentList.append(url)

            AxcGCD.Main {
                finishBlock()
            }
        }
    }

    /// 继续采集视频
    func continueCollectVideo() {
        writerState = .being

        guard let output_orientation = output_orientation,

              let output_articulation = output_articulation,

              let output_aspectRatio = output_aspectRatio,

              let output_encodingLiveVideo = output_encodingLiveVideo,

              let output_shouldCollectAudio = output_shouldCollectAudio else { return }

        beginCollect(fileUrl: temporaryPath(),

                     orientation: output_orientation,

                     articulation: output_articulation,

                     aspectRatio: output_aspectRatio,

                     encodingLiveVideo: output_encodingLiveVideo,

                     shouldCollectAudio: output_shouldCollectAudio)
    }

    /// 停止采集视频
    /// - Parameter block: 回调文件地址
    func endCollectVideo(_ block: @escaping AxcBlock.OneParam<URL>) {
        // 设置视频旋转

        guard let orientation = output_orientation else { return }

        var transform: CGAffineTransform?

        switch orientation {
        case let .transform(trans):

            transform = trans

        case let .orientation(orientation):

            var angle: Int = 0

            switch orientation {
            case .portrait: angle = 0 // 直立

            case .landscapeRight: angle = 90 // 右

            case .portraitUpsideDown: angle = 180 // 反向

            case .landscapeLeft: angle = 270 // 左

            default: break
            }

            let radians = CGFloat(angle).axc.angleToRadian

            transform = CGAffineTransform(rotationAngle: radians)
        }

        guard let transform = transform else { return }

        // 状态机

        if writerState == .being { // 进行中，未进行暂停，则需要先暂停保存最后一段
            pauseCollectVideo { [weak self] in

                guard let weakSelf = self else { return }

                weakSelf.mergeVideos(transform: transform, block: block)
            }

        } else { // 已进行过暂停保存最后一段，直接合成
            mergeVideos(transform: transform, block: block)
        }

        writerState = .end
    }
}

// MARK: - [VideoWriter]

class VideoWriter: NSObject {
    // Open

    /// 添加写入器
    var addWriterBlock: AxcBlock.OneParam<GPUImageMovieWriter>?

    /// 移除写入器
    var removeWriterBlock: AxcBlock.Empty?

    // Internal

    var writerState: WriterState = .end

    /// 输出地址
    var output_orientation: Orientation?
    var output_articulation: VideoBuffer.Articulation?
    var output_aspectRatio: Eyepiece.AspectRatio?
    var output_encodingLiveVideo: Bool?
    var output_shouldCollectAudio: Bool?

    /// 临时写入路径
    func temporaryPath() -> URL {
        return URL(fileURLWithPath: "\(NSTemporaryDirectory())/temporaryVideo_\(videoFragmentList.count).mp4")
    }

    /// 开始采集
    func beginCollect(fileUrl: URL,

                      orientation: Orientation = .orientation(.portrait),

                      articulation: VideoBuffer.Articulation,

                      aspectRatio: Eyepiece.AspectRatio,

                      encodingLiveVideo: Bool = true,

                      shouldCollectAudio: Bool = true) {
        // 删除文件，确保写入成功

        try? FileManager.default.removeItem(at: fileUrl)

        output_orientation = orientation

        output_articulation = articulation

        output_aspectRatio = aspectRatio

        output_encodingLiveVideo = encodingLiveVideo

        output_shouldCollectAudio = shouldCollectAudio

        if let currentGpuMovieWriter = currentGpuMovieWriter {
            output?.removeTarget(currentGpuMovieWriter)
        }

        let width = articulation.info.row_p

        let height = ceil(width.axc.cgFloat * aspectRatio.ratioHW).axc.int

        let videoSize: CGSize = .init(width: width,

                                      height: height)

        currentGpuMovieWriter = .init(movieURL: fileUrl,

                                      size: videoSize)

        guard let output = output,

              let currentGpuMovieWriter = currentGpuMovieWriter else { return }

        currentGpuMovieWriter.encodingLiveVideo = encodingLiveVideo // 对视频进行编码

        currentGpuMovieWriter.shouldPassthroughAudio = shouldCollectAudio // 音频采集

        addWriterBlock?(currentGpuMovieWriter)

        output.addTarget(currentGpuMovieWriter)

        // 采集方向

        var transform: CGAffineTransform?

        switch orientation {
        case let .transform(trans):

            transform = trans

        case let .orientation(orientation):

            var angle: Int = 0

            switch orientation {
            case .portrait: angle = 0 // 直立

            case .landscapeRight: angle = 90 // 右

            case .portraitUpsideDown: angle = 180 // 反向

            case .landscapeLeft: angle = 270 // 左

            default: break
            }

            let radians = CGFloat(angle).axc.angleToRadian

            transform = CGAffineTransform(rotationAngle: radians)
        }

        if let transform = transform {
            currentGpuMovieWriter.startRecording(inOrientation: transform)

        } else {
            currentGpuMovieWriter.startRecording()
        }
    }

    /// 停止采集
    func endCollect(_ block: @escaping AxcBlock.OneParam<URL>) {
        currentGpuMovieWriter?.finishRecording { [weak self] in

            guard let weakSelf = self,

                  let currentGpuMovieWriter = weakSelf.currentGpuMovieWriter else { return }

            block(currentGpuMovieWriter._movieURL)

            weakSelf.output?.removeTarget(currentGpuMovieWriter)

            weakSelf.currentGpuMovieWriter = nil

            weakSelf.removeWriterBlock?()
        }
    }

    /// 合成视频
    func mergeVideos(transform: CGAffineTransform,

                     block: @escaping AxcBlock.OneParam<URL>) {
        let outputUrl = URL(fileURLWithPath: "\(NSTemporaryDirectory())/merge_\(Date().timeIntervalSince1970).mp4")

        VideoMerge.merge(urls: videoFragmentList,

                         outputUrl: outputUrl,

                         transform: transform) { [weak self] url in

            guard let weakSelf = self else { return }

            block(url) // 返回合成视频的地址

            // 删除临时视频

            weakSelf.delFragmentList()

        } failureBlcok: { }
    }

    /// 删除临时存储的文件
    func delFragmentList() {
        videoFragmentList.forEach { fileUrl in

            try? FileManager.default.removeItem(at: fileUrl)
        }

        videoFragmentList = []
    }

    // Private

    /// 采集清晰度
    private var articulation: VideoBuffer.Articulation = ._720p

    /// 采集画幅
    private var aspectRatio: Eyepiece.AspectRatio = .ar_16_9

    /// 视频采集片段
    private var videoFragmentList: [URL] = []

    private var currentGpuMovieWriter: GPUImageMovieWriter?

    private weak var output: GPUImageOutput?
}

// MARK: OutputProtocol

extension VideoWriter: OutputProtocol {
    func setOutput(_ output: GPUImageOutput) {
        self.output = output

        if let currentGpuMovieWriter = currentGpuMovieWriter {
            output.addTarget(currentGpuMovieWriter)
        }
    }
}
